![]() |
![]() |
|
Insbesondere die Plattformunabhängigkeit des Kompilats ist bisher ein deutliches Argument für viele Unternehmen gewesen, nicht nur in heterogenen Umgebungen verstärkt auf Java zu setzen. Entwickeln wir eine .NET-basierte Anwendung, ähnelt der Ablauf der Kompilierung bis zum Start der Laufzeit dem unter Java. Zuerst wird ein Zwischencode erzeugt, der CPU-unabhängig ist. Die Dateiendung lautet .exe, wenn wir eine eigenstartfähige Anwendung entwickelt haben. Allerdings ist diese Datei nicht ohne weiteres lauffähig. Sie benötigt zur Laufzeit einen »Endcompiler«, der den Zwischencode in nativen, plattformspezifischen Code übersetzt. Der Zwischencode einer .NET-Anwendung wird als MSIL-Code (Microsoft Intermediate Language) oder nur kurz als IL bezeichnet, der Endcompiler als JIT-Compiler (Just-In-Time) oder auch nur kurz als JITter.
Abbildung 1.1 Der Ablauf der Entwicklung eines .NET-Programms bis zur Laufzeit 1.2.4 Die »Common Language Specification« (CLS)
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Wertetypen |
| Referenztypen |
Wertetypen werden auf dem Stack abgelegt. Zu ihnen gehören die in der Entwicklungsumgebung eingebauten ganzzahligen Datentypen und die Datentypen, die Fließkommazahlen beschreiben. Referenztypen werden hingegen auf dem Heap abgelegt. Zu ihnen gehören unter anderem die aus den Klassen erzeugten Objekte.
Obwohl Wertetypen im ersten Moment nicht den Anschein erwecken, von der .NET-Laufzeitumgebung als Objekte behandelt zu werden, ist das kein Widerspruch zu der vorher gemachten Aussage, dass .NET nur Objekte kennt. Tatsächlich erfolgt zur Laufzeit eine automatische Umwandlung von einen Werte- in einen Referenztyp durch ein Verfahren, das als Boxing bezeichnet wird.
Typen können ihrerseits Mitglieder enthalten: Felder, Eigenschaften, Methoden und Ereignisse. Dem Common Type System nur die Festlegung von Typen zuzuschreiben, würde die vielfältigen Aufgaben nur unzureichend beschreiben. Es gibt zudem die Regeln vor, nach denen die Sichtbarkeit dieser Typmitglieder festgelegt wird. Ein öffentlich deklariertes Mitglied eines vorgegebenen Typs könnte beispielsweise über die Grenzen der Anwendung hinaus sichtbar sein; andere Sichtbarkeiten beschränken ein Mitglied auf die aktuelle Anwendung oder sogar nur auf den Typ selbst.
Das vom Common Type System festgelegte Regelwerk ist grundsätzlich nichts Neues. Alle anderen Sprachen, auch die, die nicht auf .NET aufsetzen, weisen ein ähnliches Merkmal auf, um ein Typsystem in die Sprache zu integrieren. Aber es gibt einen entscheidenden Unterschied, in dem sich alle Sprachen der .NET-Umgebung vom Rest abheben: Während die Definition des Typsystems bei herkömmlichen Sprachen Bestandteil der Sprache selbst ist, wandert das .NET-Typsystem in die Laufzeitumgebung. Die Folgen sind gravierend: Kommunizieren zwei Komponenten miteinander, die in unterschiedlichen Sprachen entwickelt worden sind, sind keine Typkonvertierungen mehr notwendig, da sie auf demselben Typsystem aufsetzen.
Stellen Sie sich vor, eine Regelung durch das CTS würde es nicht geben. C# würde einen booleschen Typ definieren, der 2 Byte groß ist und C++.NET denselben Datentyp, der jedoch 4 Byte groß ist. Der uneingeschränkte Informationsaustausch wäre nicht möglich, sondern würde zu einem Merkmal der Sprache degradiert. Im gleichen Moment würde das ansonsten sehr stabile Framework wie ein Kartenhaus zusammenbrechen – eine fundamentale Stütze wäre ihm entzogen. Dieses Dilemma ist nicht unbekannt und beschert anderen Sprachen große Schwierigkeiten, Funktionen der WinAPI-32 direkt aufzurufen, beispielsweise Visual Basic 6.0
Ein Framework ist ein Oberbegriff für ein Gerüst, mit dem Anwendungen entwickelt, kompiliert und ausgeführt werden. Es setzt sich aus verschiedenen Richtlinien und Komponenten zusammen. Sie haben in den Abschnitten 1.2.4 und 1.2.5 mit der Common Language Specification (CLS) und dem Common Type System (CTS) bereits einen Teil des .NET Frameworks kennengelernt. Wir müssen aber dieses Anwendungsgerüst noch um zwei sehr wichtige Komponenten ergänzen:
| die Common Language Runtime (CLR) |
| die .NET-Klassenbibliothek |
Sie können in manchen Veröffentlichungen noch weitere Komponentenangaben finden, beispielsweise ADO.NET und ASP.NET. Es ist wohl mehr eine Sache der Definition, wo die Grenzen eines Frameworks gesetzt werden, da dieser Begriff nicht so klar umrissen ist. Die .NET-Klassenbibliothek ihrerseits stellt einen Oberbegriff dar, unter dem sich sowohl ADO.NET als auch ASP.NET eingliedern lassen.
Die Common Language Runtime (CLR) ist die Umgebung, in der die .NET-Anwendungen ausgeführt werden – gewissermaßen die allen gemeinsame Laufzeitschicht. Der Stellenwert dieser Komponente kann nicht hoch genug eingestuft werden, denn mit ihren Fähigkeiten bildet die CLR den Kern von .NET.
Die CLR ist ein Verwalter. Dieses Wort ins Englische übersetzt heißt Manager. Tatsächlich wird der Code, der in der Common Language Runtime ausgeführt wird, auch als verwalteter Code bezeichnet – oder im Englischen als managed code. Umgekehrt kann mit dem Visual Studio 2005 auch unverwalteter Code geschrieben werden. In unverwaltetem oder unmanaged Code sind beispielsweise Treiberprogramme geschrieben, die direkt auf die Hardware zugreifen und deshalb plattformabhängig sind.
Sie müssen sich die Common Language Runtime nicht als eine Datei vorstellen, der eine bestimmte Aufgabe im .NET-Framework zukommt, wenn verwalteter Code ausgeführt wird. Vielmehr beschreibt die CLR zahlreiche Dienste, die als Bindeglied zwischen dem verwalteten IL-Code und der Hardware den Anforderungen des .NET Frameworks entsprechen und diese sicherstellen:
| den Class Loader, der Klassen in die Laufzeitumgebung lädt |
| den Type Checker zur Unterbindung unzulässiger Typkonvertierungen |
| den JITter, der den MSIL-Code zur Laufzeit in nativen Code übersetzt, der im Prozessor ausgeführt werden kann |
| den Exception Manager, der die Ausnahmebehandlung unterstützt |
| den Garbage Collector, der eine automatische Speicherbereinigung anstößt, wenn Objekte nicht mehr benötigt werden |
| den Code Manager, der die Ausführung des Codes verwaltet |
| die Security Engine die sicherstellt, dass der User über die Berechtigung verfügt, den angeforderten Code auszuführen |
| die Debug Machine zum Debuggen der Anwendung |
| den Thread Service zur Unterstützung multithreadingfähiger Anwendungen |
| den COM Marshaller zur Sicherstellung der Kommunikation mit COM-Komponenten (COM = Component Object Model) |
Die Liste ist zwar lang, vermittelt aber einen Einblick in die verschiedenen unterschiedlichen Aufgabenbereiche der Common Language Runtime.
Das .NET Framework, das inzwischen in der Version 2.0 vorliegt, ist ausnahmslos objektorientiert ausgerichtet. Für Entwickler, die sich bisher erfolgreich dem objektorientierten Konzept widersetzt haben und mit sturer Beharrlichkeit auf prozeduralen Code gesetzt haben (solche gibt es häufiger, als Sie vielleicht vermuten), fängt die Zeit des Umdenkens an – es führt unter .NET kein Weg mehr daran vorbei.
Alles im .NET Framework wird als Objekt betrachtet. Dazu zählen sogar die nativen Datentypen der Common Language Specification wie der Integer. Die Folgen sind weitreichend, denn schon mit einer einfachen Deklaration wie
| int iVar; |
erzeugen wir ein Objekt mit allen sich daraus ergebenden Konsequenzen. Wir werden darauf in einem der folgenden Kapitel noch zu sprechen kommen.
Die .NET-Klassen stehen nicht zusammenhangslos im Raum, wie beispielsweise die Funktionen der WinAPI-32, sondern befinden sich ausnahmslos in einer engen Beziehung, der .NET-Klassenhierarchie. Eine Klassenhierarchie können Sie sich wie einen Familienstammbaum vorstellen, beim dem sich, ausgehend von einer Person, alle Nachkommen abbilden lassen. Auch die .NET-Klassenhierarchie hat einen Ausgangspunkt, gewissermaßen die Wurzel der Hierarchie: Es ist die Klasse Object. Jede andere Klasse des .NET Frameworks kann darauf zurückgeführt werden und erbt daher deren Methoden. Außerdem kann es weitere Nachfolger geben, die sowohl die Charakteristika der Klasse Object erben als auch die ihrer direkten Vorgängerklasse. Auf diese Weise bildet sich eine mehr oder weniger ausgeprägte Baumstruktur.
Für Visual C++-Programmierer ist eine Klassenhierarchie nichts Neues. Sie arbeiten bereits seit vielen Jahren mit der MFC (Microsoft Foundation Classes). Auch Java-Programmierer haben sich an eine ähnliche Hierarchie gewöhnen müssen.
Eine Klassenhierarchie basiert auf einer Bibliothek, die strukturiert ihre Dienste zum Wohle des Programmierers bereitstellt und letztendlich die Programmierung vereinfacht. Um allerdings in den Genuss der Klassenbibliothek zu kommen, muss man zuvor einiges tun – es ist ein erhöhter Lernaufwand erforderlich. Wenn man aber aus dieser Phase heraus ist, kann man sehr schnell und zielorientiert entwickeln, die anfänglichen Investitionen zahlen sich schnell aus.
Einen kurzen Überblick über den Inhalt der .NET-Klassenbibliothek zu geben, ist nur schwer, wenn nicht sogar vollkommen unmöglich, denn es handelt sich dabei um einige tausend vordefinierte Typen. Wenn man sich jetzt vorstellt, dass in jeder Klasse mehr oder weniger viele Methoden definiert sind, also Funktionen im prozeduralen Sinn, ist man sehr schnell bei Größenordnungen von einigen zehntausend Methoden, die insgesamt von den Klassen veröffentlicht werden. Alle zu kennen dürfte nicht nur an die Grenze der Unmöglichkeit stoßen, sondern diese sogar deutlich überschreiten. Außerdem kann man davon ausgehen, dass im Laufe der Zeit immer weitere Klassen mit zusätzlichen und verfeinerten Features in die Klassenhierarchie integriert werden – sowohl durch Microsoft selbst, als auch durch Drittanbieter.
Da jede Anwendung von Funktionalitäten lebt und der Zugriff auf die Klassenbibliothek zum täglichen Brot eines .NET-Entwicklers gehört, ist ein guter Überblick über die Klassen und insbesondere über deren Handling im Programmcode sehr wichtig. Hier kommt uns ein Feature entgegen, das die Arbeit deutlich erleichtert: Es sind die Namespaces. Ein Namespace ist eine logische Organisationsstruktur, die völlig unabhängig von der Klassenhierarchie eine Klasse einem bestimmten thematischen Gebiet zuordnet. Damit wird das Auffinden einer Klasse, die bestimmte Leistungsmerkmale aufweist, deutlich einfacher. Das Konzept ist natürlich auch nicht ganz neu. Ob Java wieder Pate gestanden hat, wissen wir nicht. Aber in Java gibt es eine ähnliche Struktur, die als Package bezeichnet wird.
Das Auffinden einer bestimmten Klasse ist nur ein Argument, was für die Namespaces spricht. Einem zweiten kommt eine ebenfalls nicht zu vernachlässigende Bedeutung zu. Jede Klasse ist durch einen Namen gekennzeichnet, der im Programmcode dazu benutzt wird, um daraus möglicherweise ein Objekt zu erzeugen und auf dessen Funktionalitäten zuzugreifen. Der Name muss natürlich eindeutig sein, schließlich können Sie auch nicht erwarten, dass ein Brief, der nur an Hans Fischer adressiert ist, tatsächlich den richtigen Empfänger erreicht. Namespaces verhindern Kollisionen zwischen identischen Klassenbezeichnern. Sie sind vergleichbar mit der vollständigen Adressierung eines Briefes. Nur innerhalb eines vorgegebenen Namespaces muss ein Klassenname eindeutig sein.
Die Namespaces sind auch wieder in einer hierarchischen Struktur organisiert. Machen Sie aber nicht den Fehler, die Klassenhierarchie mit der Hierarchie der Namespaces zu verwechseln. Eine Klassenhierarchie wird durch die Definition der Klasse im Programmcode festgelegt und hat Konsequenzen für die Fähigkeiten einer Klasse, bestimmte Operationen ausführen zu können, während die Zuordnung zu einem Namespace keine Auswirkungen auf die Fähigkeiten eines Objekts einer Klasse hat. Dass Klassen, die einem bestimmten Namespace zugeordnet sind, auch innerhalb der Klassenhierarchie eng zusammenstehen, ist eine Tatsache, die aus den Zusammenhängen resultiert, aber es ist kein Muss.
Wenn die Aussage, dass Namespaces in einer baumartigen Struktur organisiert werden zutrifft, muss es auch eine Wurzel geben. Diese heißt im .NET Framework System. Dieser Namespace organisiert die fundamentalsten Klassen in einem Verbund. Weiter oben habe ich erwähnt, dass sogar die nativen Datentypen wie der Integer auf Klassendefinitionen basieren – im Namespace System ist diese Klasse neben vielen weiteren zu finden. (Anmerkung: Falls Sie die Klasse jetzt aus Neugier suchen sollten – sie heißt nicht Integer sondern Int32.)
Unterhalb von System sind die anderen Namespaces angeordnet und namentlich so gegliedert, dass man schon erahnen kann, über welche Fähigkeiten die einem Namespace zugeordneten Klassen verfügen. Um ein Gefühl hierfür zu bekommen, sind in der nachfolgenden Tabelle auszugsweise ein paar Namespaces angeführt.
| Namespace | Beschreibung |
| System.Collections | Klassen, die Objektarrays beschreiben |
| System.Data | Enthält die Klassen, für den Zugriff auf Datenbanken über ADO.NET |
| System.Drawing | Klassen, die grafische Funktionalitäten bereitstellen |
| System.IO | Klassen für Ein- und Ausgabeoperationen |
| System.Web | Enthält Klassen, die im Zusammenhang mit dem Protokoll HTTP stehen |
| System.Windows.Forms | Enthält Klassen, um Windows-basierte Anwendungen zu entwickeln |
Die Tabelle gibt kaum mehr als einen Bruchteil aller .NET-Namespaces wieder. Sie sollten allerdings erkennen, wie hilfreich diese Organisationsstruktur bei der Entwicklung einer Anwendung sein kann. Suchen Sie die Lösung zu einem Problem, dann kanalisieren die Namespaces Ihre Suche und tragen so zu einer effektiveren Entwicklung bei.
Wir können in diesem Buch natürlich nicht alle Namespaces, geschweige denn alle Klassen des .NET-Frameworks behandeln. Ob das überhaupt jemals ein Buch zu leisten vermag, darf mehr als nur angezweifelt werden – zu umfangreich ist die Klassenbibliothek.
Sie sollten die wichtigsten Klassen und Namespaces kennen. Was zu den wichtigsten Komponenten gezählt werden kann, ist naturgemäß subjektiv. Ich werde mich daher auf diejenigen konzentrieren, die praktisch in jeder Anwendung von Belang sind bzw. bei jeder eigenen Klassendefinition in die Überlegung einbezogen werden müssen. In diesem Sinne werde ich mich auf die fundamentalen Bibliotheken beschränken, einschließlich der, die zur Entwicklung einer Windowsanwendung notwenig sind.
| << zurück |
|
||||||||||||||
|
||||||||||||||
|
||||||||||||||
|
||||||||||||||
Copyright © Galileo Press 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.